package org.flashbrighton.as3bots.controllers
{
	import flash.display.*;
	import flash.events.*;
	import flash.geom.Point;
	import org.flashbrighton.as3bots.bots.*;
	import org.flashbrighton.as3bots.components.*;
	import org.flashbrighton.as3bots.effects.*;
	import org.flashbrighton.as3bots.events.*;
	import org.flashbrighton.as3bots.models.*;
	import org.flashbrighton.as3bots.views.*;
	
	
	public class GameController extends Sprite
	{
		private var _model:GameModel;
		private var _view:GameView;
		private var _bots:Vector.<BaseBot>;
		private var _componentManager:ComponentManager;
		private var _arenaController:ArenaController;
		private var _matchController:MatchController;
		private var _scorer:CollisionScorer;
		private var _currentArenaWidth:Number;
		
		
		public function GameController(model:GameModel, view:GameView):void
		{
			_model = model;
			_view = view;
			init();
		}
		
		
		private function init():void
		{
			_bots = new Vector.<BaseBot>;
			_componentManager = new ComponentManager();
			_arenaController = new ArenaController(_model, _view);
			_matchController = new MatchController(_model, _view);
			_scorer = new CollisionScorer();
			_scorer.addEventListener(BotEvent.HIT, checkCollisions, false, 0, true);
			_model.physics.world.SetContactListener(_scorer);
			_model.isPlaying = false;
			addEventListener(Event.ENTER_FRAME, update, false, 0, true);
		}
		
		
		public function addBot(bot:BaseBot):void
		{
			var botModel:BotModel;
			
			// Add components
			for each (var s:String in bot.components)
			{
				_componentManager.addToBot(s, bot);
			}
			
			botModel = new BotModel(_model.physics.addCircle(0, 0, _componentManager.chassis.getRadiusFromSize(bot.size), _componentManager.chassis.density, _componentManager.chassis.friction, _componentManager.chassis.restitution));
			botModel.bot = bot;
			botModel.energy = HealthComponent.MAX_ENERGY;
			botModel.body.SetLinearDamping(_componentManager.chassis.linDamping);
			botModel.body.SetAngularDamping(_componentManager.chassis.angDamping);
			
			// Set reference back to model from body
			botModel.body.SetUserData(botModel);
			
			_bots.push(bot);
			_model.addBot(botModel);
		}
		
		
		public function setupGame():void
		{
			_currentArenaWidth = 10;
			_arenaController.createWalls(_currentArenaWidth);
			_arenaController.arrangeBots(_currentArenaWidth, _currentArenaWidth);
			
			addViews();
			_view.update();
			
			updateBotControllers();
			
			_matchController.addEventListener(MatchController.END, endGame);
			_matchController.timer.addEventListener(TimerEvent.TIMER, shrinkArena);
			_matchController.timer.reset();
			
			_model.started = false;
		}
		
		
		public function startGame():void
		{
			_model.started = true;
			_model.isPlaying = true;
			_matchController.timer.start();
		}
		
		
		public function stopGame():void
		{
			_model.isPlaying = false;
			_matchController.timer.stop();
		}
		
		
		public function pauseGame():void
		{
			_model.isPlaying = false;
			_matchController.timer.stop();
		}
		
		
		public function resumeGame():void
		{
			_model.isPlaying = true;
			_matchController.timer.start();
		}
		
		
		private function addViews():void
		{
			var bot:BaseBot;
			var botModel:BotModel;
			
			_view.addArenaView();
			
			for each (botModel in _model.botList)
			{
				bot = botModel.bot;
				if (!bot.skin) bot.skin = _componentManager.chassis.createSkin(bot.size, bot.colour, bot.skin);
				_view.addBotView(bot.skin, botModel);
			}
			
			_view.setUI();
			_view.addCountdown(_matchController.timer, MatchController.MAX_TIME);
			_view.alert.addEventListener(Event.COMPLETE, alertCompleteHandler);
		}
		
		
		private function alertCompleteHandler(e:Event):void
		{
			if (_model.started) resumeGame();
		}
		
		
		private function shrinkArena(e:TimerEvent):void
		{
			var shrinkRate:Number = 0.25;
			var minWidth:Number = 3.0;
			var interval:Number = 20;
			var dangerTime:int = 20;
			var remaining:int = MatchController.MAX_TIME - _matchController.timer.currentCount;
			
			if (_currentArenaWidth <= minWidth) return;
			
			if (remaining == dangerTime)
			{
				pauseGame();
				_view.alert.blink("DANGER");
			}
			else if (remaining % interval == 0 && remaining > dangerTime)
			{
				pauseGame();
				_view.alert.blink("SHRINKING ARENA");
			}
			
			if (remaining % interval == 0 || remaining <= dangerTime)
			{
				_arenaController.destroyWalls();
				_arenaController.createWalls(_currentArenaWidth -= shrinkRate);
			}
		}
		
		
		/**
		 * The main loop.
		 */
		private function update(event:Event=null):void
		{
			if (!_model.isPlaying) return;
			
			_model.updateWorld();
			//checkCollisions is called at this point
			_view.update();
			updateValues();
			updateComponents();
			updateBotControllers();
			_matchController.update();
		}
		
		
		private function checkCollisions(e:CollisionEvent=null):void
		{
			var botModel:BotModel = e.botModel as BotModel;
			var bot:BaseBot = botModel.bot;
			var damage:int = int(e.damage);
			
			botModel.damage = damage;
			
			// Notify Bot
			bot.controller.notify(new BotEvent(e.type, e.collisionPoint, e.targetType));
			
			// Spark effect
			var point:Point = new Point();
			point.x = botModel.body.GetPosition().x * GameView.SCALE_FACTOR - 15;
			point.y = botModel.body.GetPosition().y * GameView.SCALE_FACTOR - 15;
			_view.fx.addSpark(point, 0xFFFF00);
		}
		
		
		private function updateValues():void
		{
			var bot:BaseBot;
			var botModel:BotModel;
			var enginePower:Number;
			var steeringPower:Number;
			
			for each (botModel in _model.botList)
			{
				bot = botModel.bot;
				enginePower = bot.controller.getEnginePower();
				steeringPower = bot.controller.getSteeringPower();
				
				// Limits
				if (enginePower > 1) enginePower = 1;
				else if (enginePower < -1) enginePower = -1;
				if (steeringPower > 1) steeringPower = 1;
				else if (steeringPower < -1) steeringPower = -1;
			
				botModel.enginePower = enginePower;
				botModel.steeringPower = steeringPower;
			}
		}
		
		
		private function updateComponents():void
		{
			for each (var botModel:BotModel in _model.botList)
			{
				_componentManager.updateBot(botModel);
			}
		}
		
		
		private function updateBotControllers():void
		{
			var botModel:BotModel;
			
			for each (botModel in _model.botList)
			{
				botModel.bot.controller.update(new BotData(botModel.energy / HealthComponent.MAX_ENERGY));
			}
		}
		
		
		private function endGame(e:Event=null):void
		{
			stopGame();
			_model.started = false;
			_model.updateWorld();
			_view.update();
		}
		
		
	}
}